package org.jetlinks.pro.assets;

import lombok.NonNull;
import org.hswebframework.ezorm.core.Conditional;
import org.hswebframework.ezorm.core.NestConditional;
import org.hswebframework.web.authorization.DimensionType;
import org.springframework.core.Ordered;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Nonnull;
import java.util.*;
import java.util.function.Function;

class CompositeAssetsHolder implements AssetsHolder {

    private final List<AssetsHolder> holders;

    private final List<AssetsHolder> priorityHolders;

    public CompositeAssetsHolder(List<AssetsHolder> holders) {
        this.holders = holders;
        if (holders.size() == 1) {
            priorityHolders = holders;
        } else {
            priorityHolders = new ArrayList<>(holders.size());
            TreeMap<Integer, List<AssetsHolder>> ordered = new TreeMap<>();
            for (AssetsHolder holder : holders) {
                if (holder instanceof Ordered) {
                    ordered.computeIfAbsent(((Ordered) holder).getOrder(), order -> new ArrayList<>())
                           .add(holder);
                } else {
                    priorityHolders.add(holder);
                }
            }
            //只处理排序在前的
            priorityHolders.addAll(ordered.firstEntry().getValue());
        }
    }


    @Override
    public Flux<DimensionBinding> getDimensionBindings() {
        return Flux
            .fromIterable(holders)
            .flatMap(AssetsHolder::getDimensionBindings);
    }

    @Override
    public Flux<DimensionBinding> getDimensionBindings(DimensionType targetType) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.getDimensionBindings(targetType));
    }

    @Override
    public Flux<AssetBinding> getBindings(AssetType assetType) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.getBindings(assetType));
    }

    @Override
    public Flux<AssetBinding> getBindings(AssetType assetType, Collection<?> idList) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.getBindings(assetType, idList));
    }

    @Override
    public Flux<AssetBinding> getBindings(DimensionType targetType, AssetType assetType) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.getBindings(targetType, assetType));
    }

    @Override
    public Flux<AssetBinding> getBindings(DimensionType targetType, Collection<?> targetId, AssetType assetType) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.getBindings(targetType, targetId, assetType));
    }

    @Override
    public Flux<AssetBinding> getBindings(DimensionType targetType, Collection<?> targetIdList, AssetType assetType, Collection<?> assetIdList) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.getBindings(targetType, targetIdList, assetType, assetIdList));
    }

    @Nonnull
    @Override
    public Mono<Void> bindAssets(@NonNull String targetType,
                                 @NonNull String targetId,
                                 @NonNull String assetType,
                                 @NonNull Collection<?> assetId) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.bindAssets(targetType, targetId, assetType, assetId))
            .then();
    }

    @Nonnull
    @Override
    public Mono<Void> bindAssets(@NonNull String assetType, @NonNull Collection<?> assetId) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.bindAssets(assetType, assetId))
            .then();
    }

    @Nonnull
    @Override
    public Mono<Void> unbindAssets(@NonNull String assetType, @NonNull Collection<?> assetId) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.unbindAssets(assetType, assetId))
            .then();
    }

    @Nonnull
    @Override
    public Mono<Void> unbindAssets(@NonNull String targetType,
                                   @NonNull String targetId,
                                   @NonNull String assetType,
                                   @NonNull Collection<?> assetId) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.unbindAssets(targetType, targetId, assetType, assetId))
            .then();
    }

    @Override
    public @NonNull Mono<Boolean> hasPermission(@NonNull String assetType,
                                                @NonNull Collection<?> assetId,
                                                boolean allowAssetNotExist,
                                                @Nonnull @NonNull AssetPermission... permission) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.hasPermission(assetType, assetId, allowAssetNotExist, permission))
            .defaultIfEmpty(true)
            .any(access->access);
    }

    @Override
    public @NonNull <T> Flux<T> filter(@NonNull Flux<T> source,
                                       @NonNull AssetType assetType,
                                       @NonNull Function<T, ?> assetIdMapper,
                                       @Nonnull @NonNull AssetPermission... permission) {
        Flux<T> cache = source.cache();
        return Flux
            //只获取优先级最高的进行过滤?
            .fromIterable(this.priorityHolders)
            .flatMap(holder -> holder.filter(cache, assetType, assetIdMapper, permission))
            .distinct();
    }

    @Nonnull
    @Override
    public Mono<Void> bindAssets(@NonNull DimensionType targetType,
                                 @NonNull AssetType assetType,
                                 @NonNull Collection<?> assetId) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.bindAssets(targetType, assetType, assetId))
            .then();
    }

    @Nonnull
    @Override
    public Mono<Void> bindAssets(@NonNull AssetType assetType, @NonNull Collection<?> assetId) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.bindAssets(assetType, assetId))
            .then();
    }

    @Nonnull
    @Override
    public Mono<Void> bindAssets(@NonNull DimensionType targetType,
                                 @NonNull String targetId,
                                 @NonNull AssetType assetType,
                                 @NonNull Collection<?> assetId) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.bindAssets(targetType, targetId, assetType, assetId))
            .then();
    }

    @Override
    public Flux<DimensionBinding> getDimensionBindings(DimensionType targetType, Collection<?> targetId) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.getDimensionBindings(targetType, targetId));
    }

    @Override
    public Flux<String> refactorTopic(String topic) {
        return Flux
            .fromIterable(priorityHolders)
            .flatMap(holder -> holder.refactorTopic(topic));
    }

    @Nonnull
    @Override
    public Mono<Void> unbindAssets(@NonNull AssetType assetType, @NonNull Collection<?> assetId) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.unbindAssets(assetType, assetId))
            .then();
    }

    @Nonnull
    @Override
    public Mono<Void> unbindAssets(@NonNull String targetType,
                                   @NonNull String targetId,
                                   @NonNull AssetType assetType,
                                   @NonNull Collection<?> assetId) {
        return Flux
            .fromIterable(holders)
            .flatMap(holder -> holder.unbindAssets(targetType, targetId, assetType, assetId))
            .then();
    }

    @Override
    public <T extends Conditional<T>> NestConditional<T> injectConditional(NestConditional<T> conditional, String assetType, String idProperty) {
        NestConditional<T> next = conditional;
        //只注入优先级高的权限控制
        for (AssetsHolder holder : this.priorityHolders) {
            //多个holder使用or进行拼接
            next = holder.injectConditional(next.or(), assetType, idProperty);
        }
        return next;
    }
//
//    @Override
//    public <T extends Conditional<T>> NestConditional<T> injectDimensionConditional(NestConditional<T> conditional,
//                                                                                    String dimensionType,
//                                                                                    String idProperty) {
//        NestConditional<T> next = conditional;
//        //只注入优先级高的权限控制
//        for (AssetsHolder holder : this.priorityHolders) {
//            //多个holder使用or进行拼接
//            next = holder.injectDimensionConditional(next.or(), dimensionType, idProperty);
//        }
//        return next;
//    }
}
